/*
 * 
 * Copyright (c) 2018 Xilinx, Inc. 
 * All rights reserved.
 *
 * Author: Chris Lavin, Xilinx Research Labs.
 *
 * This file is part of RapidWright. 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
/**
 * 
 */
package com.xilinx.rapidwright.design;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;

import com.xilinx.rapidwright.device.Device;
import com.xilinx.rapidwright.edif.EDIFCell;
import com.xilinx.rapidwright.edif.EDIFPort;

/**
 * Generates Verilog or VHDL stub files to instantiate 
 * design checkpoint files.
 * 
 * Created on: Feb 7, 2018
 */
public class RTLStubGenerator {

	public static final String VERILOG_COMMENT = "//";
	public static final String VHDL_COMMENT = "--";
	
	public static EDIFPort[] getSortedPortList(EDIFCell c){
		String[] portNames = new String[c.getPorts().size()];
		int idx = 0;
		for(EDIFPort p : c.getPorts()){
			portNames[idx++] = p.getBusName();
		}
		Arrays.sort(portNames);
		EDIFPort[] ports = new EDIFPort[portNames.length];
		for(int i=0; i < portNames.length; i++){
			ports[i] = c.getPort(portNames[i]);
		}
		return ports;
	}
	
	private static String getHeaderCommentString(){
		return "This file was generated by RapidWright " + Device.RAPIDWRIGHT_VERSION + ".";
	}
	
	public static void createVerilogStub(Design d, OutputStream out){
		EDIFPort[] ports = getSortedPortList(d.getNetlist().getTopCell());
		try {
			out.write((VERILOG_COMMENT + " " + getHeaderCommentString() + "\n").getBytes());
			out.write("\n".getBytes());
			out.write((VERILOG_COMMENT + " This empty module with port declaration file causes synthesis tools to infer a black box for IP.\n").getBytes());
			out.write((VERILOG_COMMENT + " Please paste the declaration into a Verilog source file or add the file as an additional source.\n").getBytes());
			out.write(("module " + d.getName() +"(").getBytes());
			for(int i=0; i < ports.length; i++){
				if(i>0) out.write(", ".getBytes());
				out.write(ports[i].getBusName().getBytes());
			}
			out.write(");\n".getBytes());
			for(int i=0; i < ports.length; i++){
				EDIFPort p = ports[i];
				String dir = p.getDirection().name().toLowerCase();
				String range = p.getWidth() == 1 ? "" : "[" + p.getLeft() + ":" + p.getRight() +"]";
				out.write(("  " + dir + " " + range + p.getBusName() + ";\n").getBytes());
			}
			out.write("endmodule\n".getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void createVHDLStub(Design d, OutputStream out){
		EDIFPort[] ports = getSortedPortList(d.getNetlist().getTopCell());
		String topCellName = d.getNetlist().getTopCell().getName();
		try {
			out.write((VHDL_COMMENT + " " + getHeaderCommentString() + "\n").getBytes());
			out.write("library IEEE;\n".getBytes());
			out.write("use IEEE.STD_LOGIC_1164.ALL;\n\n".getBytes());
			out.write(("entity " + topCellName + " is\n").getBytes());
			out.write("  Port (\n".getBytes());
			for(int i=0; i < ports.length; i++){
				EDIFPort p = ports[i];
				String dir = p.getDirection().name().toLowerCase().replace("put", "");
				String type = " STD_LOGIC";
				if(p.getWidth() > 1){
					String endian = p.getLeft() > p.getRight() ? " downto " : " to ";
					type = " STD_LOGIC_VECTOR ( " + p.getLeft() + endian + p.getRight() + " )";
				}
				String semi = i == ports.length-1 ? "\n" : ";\n";
				out.write(("    " + p.getBusName() + " : " + dir  + type + semi).getBytes());
			}
			out.write("  );\n\n".getBytes());
			out.write(("end "+ topCellName+";\n\n").getBytes());
			out.write(("architecture stub of "+topCellName+" is\n").getBytes());
			out.write("attribute syn_black_box : boolean;\n".getBytes());
			out.write("attribute black_box_pad_pin : string;\n".getBytes());
			out.write("attribute syn_black_box of stub : architecture is true;\n".getBytes());
			out.write("begin\n".getBytes());
			out.write("end;\n".getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
}
